import redis
from datetime import datetime
from rest_framework.views import APIView
from shopping.models import Product
from rest_framework import status
from services import ResHelper, MyPaginator, FakeRedis
from shopping.serializer import *


# Create your views here.

class Products(APIView):
    def get(self, request):
        # 查询商品
        data = ProductSearchSerializer(data=request.GET)
        data.is_valid(raise_exception=True)
        filters = data.validated_data

        # name用模糊搜索
        if filters.get("name"):
            filters["name__icontains"] = filters.pop("name")
        if filters.get("description"):
            filters["description__icontains"] = filters.pop("description")
        
        # 走缓存
        redis = FakeRedis()
        if "products" in redis.keys():
            products = redis.get("products")
            
            if filters:
                products = [product for product in products if all([(value in product.get(key.split("__")[0]) if key in ["name__icontains", "description__icontains"] else product.get(key) == value) for key, value in filters.items()])]
            
            # 按照id倒序
            products = sorted(products, key=lambda x: x["id"], reverse=True)

            page = int(request.GET.get("page", 1))
            page_size = int(request.GET.get("page_size", 10))
            count = len(products)
            total = (count + page_size - 1) // page_size
            page_info = {
                "page": page,
                "total": total,
                "page_size": page_size,
                "count": count,
                "has_pre": page > 1,
                "has_next": page < total,
                "cache": True
            }

            return ResHelper.success(data=products[(page-1)*page_size:page*page_size], status=status.HTTP_200_OK, **page_info)

        products = Product.objects.filter(**filters).order_by("-id")
        paginator = MyPaginator(request,products)
        paginated_qs = paginator.get_data()

        return ResHelper.success(data=ProductSerializer(paginated_qs, many=True).data, status=status.HTTP_200_OK, **paginator.get_page_info())
        
    
    def post(self, request):
        # 新增商品
        serializer = ProductSerializer(data=request.data)
        if serializer.is_valid():
            product = serializer.save()
            return ResHelper.success(data={"id": product.id}, status=status.HTTP_201_CREATED)
        return ResHelper.error(data=serializer.errors, status=status.HTTP_400_BAD_REQUEST)
    

    def delete(self, request):
        # 删除根据id，批量删除商品
        ids = request.data.get("ids")
        Product.objects.filter(id__in=ids).delete()
        return ResHelper.success(data={"message": f"delete id: {','.join(map(str,ids))}"}, status=status.HTTP_200_OK)  

    def put(self, request):
        # 编辑商品
        try:
            id = request.data.get('id')
            product = Product.objects.get(id=id)
        except Product.DoesNotExist:
            return ResHelper.error(data={"message": f"id:{id} 不存在"}, status=status.HTTP_404_NOT_FOUND)

        update_data = request.data
        update_data.pop('id')

        serializer = ProductSerializer(product, data=update_data, partial=True)
        if serializer.is_valid():
            serializer.save(updated_at=datetime.now())  # 手动更新更新时间
            return ResHelper.success(data={"message": f"成功更新商品 id:{id}"}, status=status.HTTP_200_OK)
        return ResHelper.error(data=serializer.errors, status=status.HTTP_400_BAD_REQUEST)
        
# 模拟缓存接口，实际项目中可以结合redis等缓存工具，本项目使用fake_redis.json文件模拟redis
# 可以考虑使用定时任务进行缓存，本项目使用接口模拟
class DoCache(APIView):
    def post(self, request):
        products = Product.objects.order_by("-id")
        redis = FakeRedis()
        redis.set("products", ProductSerializer(products, many=True).data)

        return ResHelper.success(data={"message": "success"}, status=status.HTTP_200_OK)

# 将库存信息缓存在Redis中（格式如 product_stock:{id}）。
# 使用Redis的Lua脚本原子执行库存扣减操作，避免竞态条件。
# 数据库作为最终一致性的落地存储（可采用异步持久化或定时同步策略）。
class OrderProduct(APIView):
    def post(self, request):
        product_id = request.data.get("id")
        quantity = int(request.data.get("quantity", 1))

        redis_conn = redis.StrictRedis(host='localhost', port=6379, db=0)
        stock_key = f"product_stock:{product_id}"

        # 检查缓存中是否存在该商品
        # 如果不存在，则查数据库并写入缓存；
        if not redis_conn.exists(stock_key):
            product = Product.objects.get(id=product_id)
            redis_conn.set(stock_key, product.stock)

        # 在Redis中使用Lua脚本进行原子性库存扣减判断，避免超卖
        lua_script = """
        local stock = tonumber(redis.call('GET', KEYS[1]))
        if stock and stock >= tonumber(ARGV[1]) then
            return redis.call('DECRBY', KEYS[1], ARGV[1])
        else
            return -1
        end
        """

        result = redis_conn.eval(lua_script, 1, stock_key, quantity)

        if result != -1:
            # 此处建议异步记录订单，并异步落库存到数据库，可以使用celery
            # 本项目在此处直接减库存，区别仅为启动一个Celery worker，用decrby.delay()替代decrby()即可，函数运行结果可用delaty_id查询
            # delay_id = decrby.delay(product_id, quantity)
            jud,remain = self.decrby(product_id, quantity)
            if jud:
                return ResHelper.success(data={"message": f"下单成功，剩余库存：{remain}"}, status=status.HTTP_200_OK)
            return ResHelper.error(data={"message": f"下单失败，请联系管理员"}, status=status.HTTP_400_BAD_REQUEST)
        return ResHelper.error(data={"message": "库存不足，无法下单"}, status=status.HTTP_400_BAD_REQUEST)
    
    # 仅需将下面的函数加入到celery创建的保存函数的文件夹中，取消注释装饰器即可
    # @celery.task
    def decrby(self,pro_id,quantity):

        product = Product.objects.get(id=pro_id)
        remain = product.stock - quantity
        update_data = {"stock":remain}

        serializer = ProductSerializer(product, data=update_data, partial=True)
        if serializer.is_valid():
            serializer.save()
            return True, remain
        return False,0